"""The servers set up to handle various requests"""

import json
import os

from twisted.internet import reactor
from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import Factory

class ScorerProtocol(LineReceiver):
    """Gives the score of a list of elements out to those that ask"""
    def lineReceived(self, line):
        if line[:8]=='PROFILE:':
            self.profileName=line[9:]
            self.score=0
        elif line=='END':
            self.sendLine(str(self.score))
        else:
            self.score+=self.factory.scoreLine(self.profileName, line)


class ScorerFactory(Factory):
    """Scores the set of elements passed to it"""
    protocol=ScorerProtocol
    def __init__(self, profiles):
        self.profiles=profiles
        self.averageScores=dict((name, self.__averageScore(profile))
                                for name, profile in profiles.iteritems())
    def scoreLine(self, profileName, line, ratingAtt='Rating'):
        testElement=json.loads(line)
        profile=self.profiles[profileName]
        for element in profile.elements:
            if all(testElement[att]==element[att]
                   for att in profile.uniqueAtts):
                return int(element[ratingAtt])-self.averageScores[profileName]
        return 0
    @staticmethod
    def __averageScore(profile, ratingAtt='Rating'):
        for element in profile.elements:
            if ratingAtt not in element:
                element[ratingAtt]=0
        total=sum(int(element[ratingAtt]) for element in profile.elements)
        return total/len(profile.elements)

class SenderProtocol(LineReceiver):
    def lineReceived(self, line):
        for element in self.factory.getElements(line):
            self.sendLine(json.dumps(element))
        self.sendLine('END')

class SenderFactory(Factory):
    protocol=SenderProtocol
    def __init__(self, profiles, ratingAtt='Rating'):
        self.elements={}
        for name, profile in profiles.iteritems():
            try:
                elements=profile.elements
                average=sum(int(element[ratingAtt]) for element in elements)
                average/=len(elements)
                self.elements[name]=[element for element in elements
                                     if int(element[ratingAtt])>average]
            except KeyError:
                pass
    def getElements(self, profileName):
        return self.elements[profileName]
        
class DownloadProtocol(LineReceiver):
    """Sends elements on request"""
    def dataReceived(self, data):
        profileName, testElement=json.loads(data)
        try:
            ext, fileSize, inFile=self.factory.getData(profileName,
                                                       testElement)
            self.sendLine(json.dumps([ext, fileSize]))
            reactor.callLater(.01, self.transferPacket, inFile)
        except IOError:
            self.transport.loseConnection()
    def transferPacket(self, inFile):
        """Sends a packet, and sets another to be sent soon"""
        readLen=self.MAX_LENGTH-384 #Safety padding
        data=inFile.read(readLen)
        if data:
            self.transport.write(data)
            reactor.callLater(.01, self.transferPacket, inFile)

class DownloadFactory(Factory):
    protocol=DownloadProtocol
    def __init__(self, profiles):
        self.profiles=profiles
    def getData(self, profileName, testElement):
        for element in self.profiles[profileName].elements:
            if all(element[att]==testElement[att] for att in
                   self.profiles[profileName].uniqueAtts):
                return (os.path.splitext(element['Location'])[1],
                        os.path.getsize(element['Location']),
                        open(element['Location'], 'rb'))

def listen(profiles, preferences):
    reactor.listenTCP(preferences['Scorer Port'], ScorerFactory(profiles))
    reactor.listenTCP(preferences['Sender Port'], SenderFactory(profiles))
    reactor.listenTCP(preferences['Download Port'], DownloadFactory(profiles))
